package es.duma.search;

import es.duma.search.universal.MultimediaRepository;
import es.duma.search.universal.MultimediaRepositoryHome;
import es.duma.search.universal.MultimediaRepositoryLocal;
import es.duma.search.universal.MultimediaRepositoryLocalHome;
import es.duma.util.config.Configurator;
import es.duma.util.log.Log;

import es.duma.util.xmlparse.search.DumaSearch;

import es.duma.util.xsl.TransformXSL;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.util.ArrayList;
import java.util.Hashtable;

import java.util.Iterator;

import java.util.List;

import javax.ejb.EJBException;
import javax.ejb.FinderException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import javax.rmi.PortableRemoteObject;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;

/**
 * Bean del EJB de sesion que implementa el servicio de busqueda.
 * Se obtien la lista re repositorios configurada en el sistema y se lanzan las peticiones a los
 * modulos de acceso que esten activos.
 *
 * El conjunto de resultados se une en un solo fichero de descripcion de contenidos (Formato Universal DUMA) y
 * se devuleve al agente que ha invocado la busqueda.
 */
public class SearchServiceBean implements SessionBean {
   
    /** Contexto para invocar los EJB de entidad que implementan los modulos de acceso a cada repositorio */
    private SessionContext _context;
 
    /** Metodo create del EJB*/   
    public void ejbCreate() {
    }
    
    /** Se fija en contexto pasado por parametro*/
    public void setSessionContext(SessionContext context) throws EJBException {
        _context = context;
    }

    public void ejbRemove() throws EJBException {   
    }

    public void ejbActivate() throws EJBException {
    }

    public void ejbPassivate() throws EJBException {
    }
    
    /**
     * Metodo principal de busqueda.
     * Recibe por parametro un xml con la busqueda en el formato que se ha definido.
     * Se valida el xml y se realiza el "unmarshaller" con la libreria generada con JAXB2 hacia el objeto dumaSearch
     * Se lanza la peticion de busqueda a cada uno de los modulos de acceso activos (se entrega el objeto dumaSearch a cada implementacion)
     * Se realiza el merge del conjunto total de resultados
     */
    public String search(String xmlSearch) {
        Log.debug(this,"Peticion de busqueda recibida"); 
        String result="";
        ArrayList results=new ArrayList();
        DumaSearch dumaSearch=null;
        
        //Obtener el xml en un InputStream
        byte[] bytes= xmlSearch.getBytes();
        InputStream in= new ByteArrayInputStream(bytes);

        //Se encapsula el xml en un objeto de tipo DumaSearch medainte Jaxb2
        JAXBContext jaxbcontext;
        try {
            jaxbcontext = JAXBContext.newInstance("es.duma.util.xmlparse.search");
            Unmarshaller unmarshaller = jaxbcontext.createUnmarshaller();
            dumaSearch=(DumaSearch)unmarshaller.unmarshal(in);
            Log.debug(this,"XML de busqueda encapsulado en el objeto.");
        } catch (JAXBException e) {
            Log.error(this,"Error al parsear el xml de busqueda. "+e);
        }
                
        // Se obtienen los repositorios configurados para la busqueda
        ArrayList repositories = RepositoryManager.getActiveRepositories();
               
        
       //Si conectamos al MultimediaRepository via RMI
        final Context context;
        try {
            //Obtener el contexto del ejb de entidad
            context = getInitialContext("oc4j");
            Log.debug(this,"Contexto creado.");
        
            final MultimediaRepositoryHome multimediaRepositoryHome = 
                (MultimediaRepositoryHome) PortableRemoteObject.narrow( context.lookup( "MultimediaRepository" ), MultimediaRepositoryHome.class );
            
            MultimediaRepository multimediaRepository;
            
           
           // Se accede a los repositorios activos obtenidos. 
           Iterator it=repositories.iterator();
           while (it.hasNext()){
                // Se obtiene el siguiente objeto repository de la lista
                Repository repository= (Repository)it.next();
                
                // Id del repositorio. Debe conicidir con el nombre de la clase que lo implementa
                String idRepository = repository.getId();
                
                // Se obtiene el numero maximo de elementos de busqueda configurado para este repositorio
                // Si no existe se cargara el maximo definido por defecto
                int maxResults=repository.getMaxResults();
                
                Log.debug(this,"Se busca el ejb de entidad de acceso al repositorio con id:"+idRepository);
                multimediaRepository= multimediaRepositoryHome.findByPrimaryKey(idRepository);
                Log.debug(this,"Se lanza la busqueda al repositorio "+idRepository+" Nº maximo resultados "+maxResults);
                result=multimediaRepository.search(dumaSearch);
                // Se anyade el resultado parcial de busqueda de un repositorio al ArrayList de resultados
                results.add(result);
           }
           

        } catch (Exception e) {
            Log.error(this,e.getLocalizedMessage());
        }

        return mergeResults(results);
    }
    
    /**
     * Este metodo se encarga de unir el conjunto de resultados obtenidos en el total de los 
     * repositorios en un unico resultado en el formato de descripcion de contenido definido para el sistema 
     * Se ordenan los contenidos por orden alfabetico del titulo
     */
    private String mergeResults(List results){
      
        //El merge se realiza mediante una transformacion xsl
        String template_merge=Configurator.getPropertieValue("MERGE_XSL");
        TransformXSL xsl_transformer=new TransformXSL(template_merge);
        OutputStream output=new ByteArrayOutputStream();
        
        String xml="<?xml version=\"1.0\" encoding=\"UTF-8\"?><merge>";
        Iterator it=results.iterator();
        while (it.hasNext()){
            xml+=(String)it.next();
        }
        xml+="</merge>";

        
        try {
            Log.debug(this,"Se inicia el merge de los resultados");
             //Obtener el xml en un InputStream
            //Log.debug(this,xml);
            byte[] bytes= xml.getBytes();
            InputStream in= new ByteArrayInputStream(bytes);
            xsl_transformer.print(in,output);
             
         } catch (IOException e) {
             Log.error(this,"Error en la transformacion "+e);
         }
        
        //Log.debug(this,"Rsultado final "+output.toString());
        return output.toString();
    }
    
   
    
    /** 
     * Se obtiene el contexto para los EJB de entidad 
     */
    private static Context getInitialContext(String server) throws NamingException {
        if (server!=null){
            Hashtable env = new Hashtable();
            //  Standalone OC4J connection details
            env.put( Context.INITIAL_CONTEXT_FACTORY, Configurator.getPropertieValue("INITIAL_CONTEXT_FACTORY"));
            env.put( Context.SECURITY_PRINCIPAL, Configurator.getPropertieValue("SECURITY_PRINCIPAL") );
            env.put( Context.SECURITY_CREDENTIALS, Configurator.getPropertieValue("SECURITY_CREDENTIALS") );
            env.put(Context.PROVIDER_URL, Configurator.getPropertieValue("PROVIDER_URL_MODULE_UNIVERSAL"));
            return new InitialContext(env);
        }
        // Get InitialContext for Embedded OC4J
        // The embedded server must be running for lookups to succeed.
        return new InitialContext();
    }
    
}
